// Description: 业务缓存插件(采用进程内的缓存实现)
package xcache

import (
	"reflect"
	"time"

	"github.com/jinzhu/copier"
)

type (
	ProcCache interface {
		// 针对列表数据的缓存（针对app端列表数据的缓存）（内部默认缓存五分钟）
		List(key string, v interface{}, fn func() (v interface{}, hasMore bool, err error)) (hasMore bool, err error)
		// 针对详情的缓存(复杂组合数据对象的详情)
		Detail(key string, data interface{}, fn func() (v interface{}, err error)) (err error)
		// 删除key
		Del(key string)
		// 所有的key
		Keys() []string
	}

	defaultProcCache struct {
		expiry         time.Duration // 过期时间
		notFoundExpiry time.Duration // 无数据的过期时间
	}

	procResult struct {
		Value   interface{}
		HasMore bool
		Expires int64 // 过期时间(秒)
	}

	spanCacheRet struct {
		Value   string
		HasMore bool
		Expires int64 // 过期时间(秒)
	}
)

func NewProcCache() ProcCache {
	return &defaultProcCache{
		expiry:         defaultExpiry,
		notFoundExpiry: defaultNotFoundExpiry,
	}
}

func NewProcCacheWithExpiry(expiry time.Duration) ProcCache {
	return &defaultProcCache{
		expiry:         expiry,
		notFoundExpiry: defaultNotFoundExpiry,
	}
}

func NewProcCacheWithExpiryIntSecond(expiry int) ProcCache {
	return &defaultProcCache{
		expiry:         time.Duration(expiry) * time.Second,
		notFoundExpiry: defaultNotFoundExpiry,
	}
}

// 针对列表数据的缓存（针对app端列表数据的缓存）（内部默认缓存五分钟）
// 内存缓存->数据查询->内存缓存设置
func (bc *defaultProcCache) List(key string, data interface{}, fn func() (interface{}, bool, error)) (bool, error) {
	value, ok := gCache.Get(key)
	if ok {
		_ = copier.Copy(data, value.(*procResult).Value)
		return value.(*procResult).HasMore, nil
	}

	value, err := gCache.Take(key, func() (interface{}, error) {
		list, hasMore, e := fn()
		if e != nil {
			if e == ErrNotFound {
				return &procResult{
					Value:   nil,
					HasMore: false,
				}, nil
			}
			return nil, e
		}

		if val := reflect.ValueOf(list); !val.IsValid() || val.IsNil() {
			return &procResult{
				Value:   nil,
				HasMore: false,
			}, nil
		}

		return &procResult{
			Value:   list,
			HasMore: hasMore,
		}, nil
	}, bc.expiry, bc.notFoundExpiry)
	if err != nil {
		return false, err
	}

	if value.(*procResult).Value == nil {
		return false, nil
	}

	_ = copier.Copy(data, value.(*procResult).Value)
	return value.(*procResult).HasMore, nil
}

// 针对详情的缓存(复杂组合数据对象的详情)
// 内存缓存->数据查询->内存缓存设置
func (bc *defaultProcCache) Detail(key string, data interface{}, fn func() (interface{}, error)) error {
	value, ok := gCache.Get(key)
	if ok {
		_ = copier.Copy(data, value.(*procResult).Value)
		return nil
	}

	value, err := gCache.Take(key, func() (interface{}, error) {
		object, e := fn()
		if e != nil {
			if e == ErrNotFound {
				return &procResult{
					Value: nil,
				}, nil
			}
			return nil, e
		}

		if val := reflect.ValueOf(object); !val.IsValid() || val.IsNil() {
			return &procResult{
				Value: nil,
			}, nil
		}

		return &procResult{
			Value: object,
		}, nil
	}, bc.expiry, bc.notFoundExpiry)
	if err != nil {
		return err
	}

	if value.(*procResult).Value == nil {
		return ErrNotFound
	}

	_ = copier.Copy(data, value.(*procResult).Value)
	return nil
}

// 删除
func (bc *defaultProcCache) Del(key string) {
	gCache.Del(key)
}

// 所有的key
func (bc *defaultProcCache) Keys() []string {
	return gCache.Keys()
}
